library(tidyverse)
library(ggthemes)
library(gridExtra)
library(ggpubr) # for ggarrange
library(langcog) # for multi_boot_standard
  
theme_set(theme_few())
# langcog package is installed by running
# install.packages("devtools")
# devtools::install_github("langcog/langcog")
# make a base plots
# for flipped coordinate system with prop correct on x
p1 <- ggplot() +
  geom_hline(yintercept = 1/3, lty = 2) +
  scale_y_continuous("Proportion Correct", limits = c(-0.05, 1.05)) +
  theme(axis.title.y = element_blank(), axis.ticks.y = element_blank())
p2 <- p1 + theme(axis.text.y = element_blank())

Intro

These are some preliminary visualizations of the data.

phylo <- read_csv("../data/species_data.csv") %>% 
  select(species, species_formatted, clade, clade_formatted, phylo)
mp_data <- read.csv("../data/merged_data/01_manyprimates_pilot_merged_data_v2.csv",
                    stringsAsFactors = F) %>%
  left_join(phylo, by = "species") %>%
  mutate(species = species_formatted,
         species = reorder(species, phylo),
         clade = clade_formatted,
         clade = fct_relevel(clade, "Lemur", "New World monkey", "Old World monkey", "Ape"),
         delay = factor(delay),
         site = fct_recode(site, 
                           "Edinburgh Zoo" = "edinburgh", 
                           "Kumamoto Sanctuary" = "kumamoto sanctuary",
                           "Wolfgang Köhler Primate\nResearch Center" = "Leipzig Zoo",
                           "Sweetwaters Chimpanzee   \nSanctuary Group 1" = "sweetwaters",
                           "Sweetwaters Chimpanzee\nSanctuary Group 2" = "sweetwaters_group2",
                           "Language Research Center" = "LRC"
                           )
)

Overview by species

First an overview of the data, plotted by species and delay. Small, transparent dots represent aggregated data for each individual by delay. Open dots are the group mean for that delay. Error bars are 95% confidence intervals. Text labels are the sample size.

plot_individual <- mp_data %>%
  group_by(phylo, clade, species, delay, subject_site) %>%
  summarise(correct = mean(correct),
            nr_trials = max(trial))
plot_group <- plot_individual %>%
   multi_boot_standard(col = "correct")
# get sample sizes (for use in plots)
ns <- mp_data %>%
  group_by(clade, species) %>%
  mutate(n = n_distinct(subject_site)) %>%
  group_by(clade, species, site, n) %>%
  summarise(n_by_site = n_distinct(subject_site))
p2 + aes(x = delay, y = correct, col = delay) +
  geom_jitter(data = plot_individual, aes(size = nr_trials), width = .1, height = .015, alpha = .15) +
  geom_point(data = plot_group, aes(y = mean), shape = 1, size = 3, stroke = 1.5) +
  geom_linerange(data = plot_group, aes(y = NULL, ymin = ci_lower, ymax = ci_upper), lwd = 1.2) +
  # geom_text(data = ns, aes(label = n), y = -.05, x = 2, col = "black", size = 3) +
  facet_grid(species ~ ., switch = "y") +
  theme(strip.text.y = element_text(angle = 180, hjust = 0)) +
  scale_size_area("Number of Trials", max_size = 3, breaks = c(12, 24, 36)) +
  scale_colour_solarized("Delay", breaks = c("short", "medium", "long")) +
  coord_flip(ylim = 0:1)

ggsave("../graphs/02_01_overview.png", width = 4, height = 4.5, scale = 2)
ggsave("../graphs/Fig2.tiff", width = 4, height = 4.5, scale = 2, type = "cairo", compression = "lzw")

Overview by species across delays

plot_individual2 <- plot_individual %>%
  group_by(clade, species, subject_site) %>%
  summarise(correct = mean(correct))
plot_group2 <- plot_individual2 %>%
   multi_boot_standard(col = "correct")
p1 + aes(x = fct_rev(species), y = correct, col = clade) +
  geom_jitter(data = plot_individual2, width = .1, height = .015, alpha = .15, size = 3) +
  geom_point(data = plot_group2, aes(y = mean), shape = 1, size = 3, stroke = 1.5) +
  geom_linerange(data = plot_group2, aes(y = NULL, ymin = ci_lower, ymax = ci_upper), lwd = 1.2) +
  geom_text(data = ns, aes(label = n), y = -.05, col = "black", size = 3) +
  facet_grid(clade ~ ., scales = "free_y", space = "free_y") +
  theme(strip.text.y = element_blank()) +
  scale_colour_solarized() +
  coord_flip()

ggsave("../graphs/02_02_overview_across_delays.png", width = 4, height = 1.8, scale = 2)

Plots by site

Here we select the species for which we have data from multiple sites. This is a very preliminary way of checking whether there is a lot of variation between sites. Plotting conventions are the same as above.

First we check for which species we have data from more than one site:

mp_data %>%
  group_by(species) %>%
  summarise(sites = n_distinct(site)) %>%
  arrange(desc(sites)) %>%
  knitr::kable()
species sites
Chimpanzee 5
Ring-tailed lemur 2
Brown capuchin monkey 2
Bonobo 2
Gorilla 2
Black-and-white ruffed lemur 1
Black-faced spider monkey 1
Squirrel monkey 1
Rhesus macaque 1
Long-tailed macaque 1
Barbary macaque 1
Orangutan 1

Chimpanzees

chimp_plot_individual <- mp_data %>%
  filter(species == "Chimpanzee") %>%
  group_by(site, delay, subject_site) %>%
  summarise(correct = mean(correct))
chimp_plot_group <- chimp_plot_individual %>%
   multi_boot_standard(col = "correct")
ch = p2 + aes(x = delay, y = correct, col = delay) +
  geom_jitter(data = chimp_plot_individual, width = .1, height = .015, alpha = .3, size = 3) +
  geom_pointrange(data = chimp_plot_group, aes(y = mean, ymin = ci_lower, ymax = ci_upper), size = .8, shape = 1, stroke = 1.5) +
  geom_text(data = filter(ns, species == "Chimpanzee"), aes(label = n_by_site), y = -.05, x = 2, col = "black", size = 3) +
  facet_grid(site ~ ., switch = "y") +
  theme(strip.text.y = element_text(angle = 180)) +
  scale_colour_solarized(breaks = c("short", "medium", "long")) +
  ggtitle("Chimpanzees") +
  coord_flip()
ch

Ring-tailed lemurs

rtlemur_plot_individual <- mp_data %>%
  filter(species == "Ring-tailed lemur") %>%
  group_by(site, delay, subject_site) %>%
  summarise(correct = mean(correct))
rtlemur_plot_group <- rtlemur_plot_individual %>%
   multi_boot_standard(col = "correct")
p2 + aes(x = delay, y = correct, col = delay) +
  geom_jitter(data = rtlemur_plot_individual, width = .1, height = .015, alpha = .3, size = 3) +
  geom_pointrange(data = rtlemur_plot_group, aes(y = mean, ymin = ci_lower, ymax = ci_upper), size = .8, shape = 1, stroke = 1.5) +
  geom_text(data = filter(ns, species == "Ring-tailed lemur"), aes(label = n_by_site), y = -.05, x = 2, col = "black", size = 3) +
  facet_grid(site ~ ., switch = "y") +
  theme(strip.text.y = element_text(angle = 180)) +
  scale_colour_solarized(breaks = c("short", "medium", "long")) +
  ggtitle("Ring-tailed lemurs") +
  coord_flip()

ggsave("../graphs/02_04_rtlemur_by_site.png", width = 4, height = 1.2, scale = 2)

Brown Capuchins

cap_plot_individual <- mp_data %>%
  filter(species == "Brown capuchin monkey") %>%
  group_by(site, delay, subject_site) %>%
  summarise(correct = mean(correct))
cap_plot_group <- cap_plot_individual %>%
   multi_boot_standard(col = "correct")
cap = p2 + aes(x = delay, y = correct, col = delay) +
  geom_jitter(data = cap_plot_individual, width = .1, height = .015, alpha = .3, size = 3) +
  geom_pointrange(data = cap_plot_group, aes(y = mean, ymin = ci_lower, ymax = ci_upper), size = .8, shape = 1, stroke = 1.5) +
  geom_text(data = filter(ns, species == "Brown capuchin monkey"), aes(label = n_by_site), y = -.05, x = 2, col = "black", size = 3) +
  facet_grid(site ~ ., switch = "y") +
  theme(strip.text.y = element_text(angle = 180)) +
  scale_colour_solarized(breaks = c("short", "medium", "long")) +
  ggtitle("Capuchin Monkeys") +
  coord_flip()
lay = matrix(c(1, 1, 2))
grid.arrange(ch, cap, layout_matrix = lay)

ggsave("../graphs/Fig5.tiff", arrangeGrob(ch, cap, layout_matrix = lay), 
       width = 4, height = 4, scale = 2, type = "cairo", compression = "lzw")
ggsave("../graphs/02_05_chimp_capuchin_by_site.png", arrangeGrob(ch, cap, layout_matrix = lay), 
       width = 4, height = 4, scale = 2)

Bonobos

bon_plot_individual <- mp_data %>%
  filter(species == "Bonobo") %>%
  group_by(site, delay, subject_site) %>%
  summarise(correct = mean(correct))
bon_plot_group <- bon_plot_individual %>%
   multi_boot_standard(col = "correct")
p2 + aes(x = delay, y = correct, col = delay) +
  geom_jitter(data = bon_plot_individual, width = .1, height = .015, alpha = .3, size = 3) +
  geom_pointrange(data = bon_plot_group, aes(y = mean, ymin = ci_lower, ymax = ci_upper), size = .8, shape = 1, stroke = 1.5) +
  geom_text(data = filter(ns, species == "Bonobo"), aes(label = n_by_site), y = -.05, x = 2, col = "black", size = 3) +
  facet_grid(site ~ ., switch = "y") +
  theme(strip.text.y = element_text(angle = 180)) +
  scale_colour_solarized(breaks = c("short", "medium", "long")) +
  ggtitle("Bonobos") +
  coord_flip()

ggsave("../graphs/02_06_bonobo_by_site.png", width = 4, height = 1.2, scale = 2)

Gorilla

gor_plot_individual <- mp_data %>%
  filter(species == "Gorilla") %>%
  group_by(site, delay, subject_site) %>%
  summarise(correct = mean(correct))
gor_plot_group <- gor_plot_individual %>%
   multi_boot_standard(col = "correct")
p2 + aes(x = delay, y = correct, col = delay) +
  geom_jitter(data = gor_plot_individual, width = .1, height = .015, alpha = .3, size = 3) +
  geom_pointrange(data = gor_plot_group, aes(y = mean, ymin = ci_lower, ymax = ci_upper), size = .8, shape = 1, stroke = 1.5) +
  geom_text(data = filter(ns, species == "Gorilla"), aes(label = n_by_site), y = -.05, x = 2, col = "black", size = 3) +
  facet_grid(site ~ ., switch = "y") +
  theme(strip.text.y = element_text(angle = 180)) +
  scale_colour_solarized(breaks = c("short", "medium", "long")) +
  ggtitle("Gorillas") +
  coord_flip()

ggsave("../graphs/02_07_gorilla_by_site.png", width = 4, height = 1.2, scale = 2)

Task-experience

Here we split each species by task experience. Check if we have species with sufficient memebers having different levels of task experience.

mp_data %>%
  group_by(species) %>%
  mutate(lvls_task_exp = n_distinct(task_experience)) %>%
  filter(lvls_task_exp > 1) %>%
  group_by(species, task_experience) %>%
  summarise(n = n_distinct(subject_site)) %>%
  knitr::kable()
species task_experience n
Chimpanzee no 19
Chimpanzee yes 32
Bonobo no 6
Bonobo yes 5
# get sample sizes (for use in plots)
ns_task_exp <- mp_data %>%
  group_by(clade, species) %>%
  mutate(n = n_distinct(task_experience)) %>%
  group_by(clade, species, task_experience, n) %>%
  summarise(n_by_site = n_distinct(subject_site))

So far, this only applies to chimps and bonobos. However, task experience co-varies with site.

chimp_task_plot_individual <- mp_data %>%
  filter(species == "Chimpanzee") %>%
  group_by(site, task_experience, delay, subject_site) %>%
  summarise(correct = mean(correct))
chimp_task_plot_group <- mp_data %>%
  filter(species == "Chimpanzee") %>%
  group_by(task_experience, delay, subject_site) %>%
  summarise(correct = mean(correct)) %>%
  multi_boot_standard(col = "correct")
p_taskexp_chimp <- p2 + aes(x = delay, y = correct) +
  geom_jitter(data = chimp_task_plot_individual, aes(col = site), width = .3, height = .015, alpha = .5, size = 3) +
  geom_pointrange(data = chimp_task_plot_group, aes(y = mean, ymin = ci_lower, ymax = ci_upper, shape = delay), size = .8, stroke = 1.5) +
  geom_text(data = filter(ns_task_exp, species == "Chimpanzee"), aes(label = n_by_site), y = -.05, x = 2, col = "black", size = 3) +
  facet_grid(task_experience ~ ., switch = "y") +
  theme(legend.box = "horizontal", strip.text.y = element_text(angle = 180)) +
  scale_shape_manual(values = c(1, 2, 5), breaks = c("short", "medium", "long")) +
  scale_colour_solarized() +
  ggtitle("Chimpanzees") +
  coord_flip()
bonobo_task_plot_individual <- mp_data %>%
  filter(species == "Bonobo") %>%
  group_by(site, task_experience, delay, subject_site) %>%
  summarise(correct = mean(correct))
bonobo_task_plot_group <- mp_data %>%
  filter(species == "Bonobo") %>%
  group_by(task_experience, delay, subject_site) %>%
  summarise(correct = mean(correct)) %>%
  multi_boot_standard(col = "correct")
p_taskexp_bon <- p2 + aes(x = delay, y = correct) +
  geom_jitter(data = bonobo_task_plot_individual, aes(col = site), width = .3, height = .015, alpha = .5, size = 3) +
  geom_pointrange(data = bonobo_task_plot_group, aes(y = mean, ymin = ci_lower, ymax = ci_upper, shape = delay), size = .8, stroke = 1.5) +
  geom_text(data = filter(ns_task_exp, species == "Bbonobo"), aes(label = n_by_site), y = -.05, x = 2, col = "black", size = 3) +
  facet_grid(task_experience ~ ., switch = "y") +
  theme(legend.box = "horizontal", strip.text.y = element_text(angle = 180)) +
  scale_shape_manual(values = c(1, 2, 5), breaks = c("short", "medium", "long")) +
  scale_colour_solarized() +
  ggtitle("Bonobos") +
  coord_flip()
grid.arrange(p_taskexp_chimp, p_taskexp_bon, ncol = 1)

grob <- arrangeGrob(p_taskexp_chimp, p_taskexp_bon, ncol = 1)
ggsave("../graphs/02_08_task_experience.png", grob, width = 4, height = 3, scale = 2)

Age

Here we plot age against correct choice separate for each delay and species. Regression line is smoothed delayal mean.

plot_age <- mp_data %>%
  mutate(delay = fct_rev(delay)) %>%
  group_by(subject_site, norm_age, clade, species, delay) %>%
  summarise(correct = mean(correct))
ggplot(plot_age, aes(x = norm_age, y = correct)) +
  geom_jitter(aes(col = clade), width = .05, height = .05, alpha = .5, size = 2.5) +
  geom_smooth(method = "glm", method.args = list(family = "binomial"), col = "black") +
  # geom_vline(xintercept = 0, lty = 2) +
  geom_hline(yintercept = 1/3, lty = 2) +
  facet_grid(~ delay) +
  labs(x = "Normed Age (relative to species longevity)", y = "Proportion Correct") +
  scale_color_solarized("Clade") +
  ylim(c(-.05, 1.05))

ggsave("../graphs/02_09_age_by_delay.png", width = 4, height = 1.3, scale = 2)
ggsave("../graphs/Fig4.tiff", width = 4, height = 1.3, scale = 2, type = "cairo", compression = "lzw")
ggplot(plot_age, aes(x = norm_age, y = correct)) +
  geom_jitter(aes(fill = species), width = .05, height = .05, alpha = .5, size = 2.5, shape = 21, stroke = 0) +
  geom_smooth(aes(col = delay), method = "glm", method.args = list(family = "binomial"), show.legend = F) +
  geom_vline(xintercept = 0, lty = 2) +
  geom_hline(yintercept = 1/3, lty = 2) +
  facet_grid(delay ~ clade, scales = "free_x") +
  labs(x = "Normed Age (relative to species longevity)", y = "Proportion Correct") +
  theme(legend.position = "bottom") +
  scale_color_manual(values = rev(solarized_palette(3))) +
  ylim(c(-.05, 1.05))

ggsave("../graphs/02_10_age_by_delay_species.png", width = 4, height = 3.4, scale = 2)

Cup distance and board size

(sig. effect in model)

plot_cup <- mp_data %>%
   group_by(subject_site, cup_distance, clade, species, delay) %>%
   summarise(correct = mean(correct))
cp <- ggplot(plot_cup, aes(x = cup_distance, y = correct)) +
  geom_jitter(aes(col = clade), width = .5, height = .05, alpha = .5, size = 2) +
  geom_smooth(method = "glm", method.args = list(family = "binomial"), col = "black") +
  labs(x = "Cup Distance in cm", y = "Proportion Correct") +
  geom_hline(yintercept = 1/3, lty = 2) +
  scale_color_solarized("Clade") +
  ylim(c(-.05, 1.05)) +
  ggtitle("A. Cup Distance")
plot_board <- mp_data %>%
   group_by(subject_site, board_size, clade, species, delay) %>%
   summarise(correct = mean(correct))
bp <- ggplot(plot_board, aes(x = board_size, y = correct)) +
  geom_jitter(aes(col = clade), width = .5, height = .05, alpha = .5, size = 2) +
  geom_smooth(method = "glm", method.args = list(family = "binomial"), col = "black") +
  labs(x = "Board Size in cm", y = "Proportion Correct") +
  geom_hline(yintercept = 1/3, lty = 2) +
  scale_color_solarized("Clade") +
  ylim(c(-.05, 1.05)) +
  ggtitle("B. Board Size")
pcb <- ggarrange(cp, bp, common.legend = T, legend = "right")
non-integer #successes in a binomial glm!non-integer #successes in a binomial glm!non-integer #successes in a binomial glm!
pcb

ggsave("../graphs/02_11_cup_distance_board_size.png", pcb, width = 8, height = 3, scale = 1.5)
LS0tCnRpdGxlOiAiTWFueVByaW1hdGVzIHBsb3RzIgphdXRob3I6ICJNYW51ZWwgQm9obiIKZGF0ZTogIk9jdCAzMSAyMDE4IgpvdXRwdXQ6CiAgaHRtbF9ub3RlYm9vazoKICAgIGNvZGVfZm9sZGluZzogaGlkZQogICAgY3NzOiBzdHlsZS5jc3MKICAgIHRoZW1lOiBwYXBlcgogICAgdG9jOiB5ZXMKICAgIHRvY19mbG9hdDogeWVzCi0tLQoKYGBge3Igc2V0dXAsIG1lc3NhZ2U9RkFMU0V9CmxpYnJhcnkodGlkeXZlcnNlKQpsaWJyYXJ5KGdndGhlbWVzKQpsaWJyYXJ5KGdyaWRFeHRyYSkKbGlicmFyeShnZ3B1YnIpICMgZm9yIGdnYXJyYW5nZQpsaWJyYXJ5KGxhbmdjb2cpICMgZm9yIG11bHRpX2Jvb3Rfc3RhbmRhcmQKICAKdGhlbWVfc2V0KHRoZW1lX2ZldygpKQoKIyBsYW5nY29nIHBhY2thZ2UgaXMgaW5zdGFsbGVkIGJ5IHJ1bm5pbmcKIyBpbnN0YWxsLnBhY2thZ2VzKCJkZXZ0b29scyIpCiMgZGV2dG9vbHM6Omluc3RhbGxfZ2l0aHViKCJsYW5nY29nL2xhbmdjb2ciKQpgYGAKCmBgYHtyfQojIG1ha2UgYSBiYXNlIHBsb3RzCiMgZm9yIGZsaXBwZWQgY29vcmRpbmF0ZSBzeXN0ZW0gd2l0aCBwcm9wIGNvcnJlY3Qgb24geApwMSA8LSBnZ3Bsb3QoKSArCiAgZ2VvbV9obGluZSh5aW50ZXJjZXB0ID0gMS8zLCBsdHkgPSAyKSArCiAgc2NhbGVfeV9jb250aW51b3VzKCJQcm9wb3J0aW9uIENvcnJlY3QiLCBsaW1pdHMgPSBjKC0wLjA1LCAxLjA1KSkgKwogIHRoZW1lKGF4aXMudGl0bGUueSA9IGVsZW1lbnRfYmxhbmsoKSwgYXhpcy50aWNrcy55ID0gZWxlbWVudF9ibGFuaygpKQoKcDIgPC0gcDEgKyB0aGVtZShheGlzLnRleHQueSA9IGVsZW1lbnRfYmxhbmsoKSkKYGBgCgojIEludHJvCgpUaGVzZSBhcmUgc29tZSBwcmVsaW1pbmFyeSB2aXN1YWxpemF0aW9ucyBvZiB0aGUgZGF0YS4KCmBgYHtyIGxvYWRpbmcgZGF0YSwgbWVzc2FnZT1GQUxTRX0KcGh5bG8gPC0gcmVhZF9jc3YoIi4uL2RhdGEvc3BlY2llc19kYXRhLmNzdiIpICU+JSAKICBzZWxlY3Qoc3BlY2llcywgc3BlY2llc19mb3JtYXR0ZWQsIGNsYWRlLCBjbGFkZV9mb3JtYXR0ZWQsIHBoeWxvKQoKbXBfZGF0YSA8LSByZWFkLmNzdigiLi4vZGF0YS9tZXJnZWRfZGF0YS8wMV9tYW55cHJpbWF0ZXNfcGlsb3RfbWVyZ2VkX2RhdGFfdjIuY3N2IiwKICAgICAgICAgICAgICAgICAgICBzdHJpbmdzQXNGYWN0b3JzID0gRikgJT4lCiAgbGVmdF9qb2luKHBoeWxvLCBieSA9ICJzcGVjaWVzIikgJT4lCiAgbXV0YXRlKHNwZWNpZXMgPSBzcGVjaWVzX2Zvcm1hdHRlZCwKICAgICAgICAgc3BlY2llcyA9IHJlb3JkZXIoc3BlY2llcywgcGh5bG8pLAogICAgICAgICBjbGFkZSA9IGNsYWRlX2Zvcm1hdHRlZCwKICAgICAgICAgY2xhZGUgPSBmY3RfcmVsZXZlbChjbGFkZSwgIkxlbXVyIiwgIk5ldyBXb3JsZCBtb25rZXkiLCAiT2xkIFdvcmxkIG1vbmtleSIsICJBcGUiKSwKICAgICAgICAgZGVsYXkgPSBmYWN0b3IoZGVsYXkpLAogICAgICAgICBzaXRlID0gZmN0X3JlY29kZShzaXRlLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgIkVkaW5idXJnaCBab28iID0gImVkaW5idXJnaCIsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAiS3VtYW1vdG8gU2FuY3R1YXJ5IiA9ICJrdW1hbW90byBzYW5jdHVhcnkiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAiV29sZmdhbmcgS8O2aGxlciBQcmltYXRlXG5SZXNlYXJjaCBDZW50ZXIiID0gIkxlaXB6aWcgWm9vIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgIlN3ZWV0d2F0ZXJzIENoaW1wYW56ZWUgICBcblNhbmN0dWFyeSBHcm91cCAxIiA9ICJzd2VldHdhdGVycyIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICJTd2VldHdhdGVycyBDaGltcGFuemVlXG5TYW5jdHVhcnkgR3JvdXAgMiIgPSAic3dlZXR3YXRlcnNfZ3JvdXAyIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgIkxhbmd1YWdlIFJlc2VhcmNoIENlbnRlciIgPSAiTFJDIgogICAgICAgICAgICAgICAgICAgICAgICAgICApCikKYGBgCgojIE92ZXJ2aWV3IGJ5IHNwZWNpZXMKCkZpcnN0IGFuIG92ZXJ2aWV3IG9mIHRoZSBkYXRhLCBwbG90dGVkIGJ5IHNwZWNpZXMgYW5kIGRlbGF5LiBTbWFsbCwgdHJhbnNwYXJlbnQgZG90cyByZXByZXNlbnQgYWdncmVnYXRlZCBkYXRhIGZvciBlYWNoIGluZGl2aWR1YWwgYnkgZGVsYXkuIE9wZW4gZG90cyBhcmUgdGhlIGdyb3VwIG1lYW4gZm9yIHRoYXQgZGVsYXkuIEVycm9yIGJhcnMgYXJlIDk1JSBjb25maWRlbmNlIGludGVydmFscy4gVGV4dCBsYWJlbHMgYXJlIHRoZSBzYW1wbGUgc2l6ZS4KCmBgYHtyIG92ZXJ2aWV3IGJ5IHNwZWNpZXMsIHdhcm5pbmc9RkFMU0UsIG1lc3NhZ2U9RkFMU0V9CnBsb3RfaW5kaXZpZHVhbCA8LSBtcF9kYXRhICU+JQogIGdyb3VwX2J5KHBoeWxvLCBjbGFkZSwgc3BlY2llcywgZGVsYXksIHN1YmplY3Rfc2l0ZSkgJT4lCiAgc3VtbWFyaXNlKGNvcnJlY3QgPSBtZWFuKGNvcnJlY3QpLAogICAgICAgICAgICBucl90cmlhbHMgPSBtYXgodHJpYWwpKQoKcGxvdF9ncm91cCA8LSBwbG90X2luZGl2aWR1YWwgJT4lCiAgIG11bHRpX2Jvb3Rfc3RhbmRhcmQoY29sID0gImNvcnJlY3QiKQpgYGAKCmBgYHtyfQojIGdldCBzYW1wbGUgc2l6ZXMgKGZvciB1c2UgaW4gcGxvdHMpCm5zIDwtIG1wX2RhdGEgJT4lCiAgZ3JvdXBfYnkoY2xhZGUsIHNwZWNpZXMpICU+JQogIG11dGF0ZShuID0gbl9kaXN0aW5jdChzdWJqZWN0X3NpdGUpKSAlPiUKICBncm91cF9ieShjbGFkZSwgc3BlY2llcywgc2l0ZSwgbikgJT4lCiAgc3VtbWFyaXNlKG5fYnlfc2l0ZSA9IG5fZGlzdGluY3Qoc3ViamVjdF9zaXRlKSkKYGBgCgpgYGB7ciwgZmlnLndpZHRoPTQsIGZpZy5oZWlnaHQ9NC41fQpwMiArIGFlcyh4ID0gZGVsYXksIHkgPSBjb3JyZWN0LCBjb2wgPSBkZWxheSkgKwogIGdlb21faml0dGVyKGRhdGEgPSBwbG90X2luZGl2aWR1YWwsIGFlcyhzaXplID0gbnJfdHJpYWxzKSwgd2lkdGggPSAuMSwgaGVpZ2h0ID0gLjAxNSwgYWxwaGEgPSAuMTUpICsKICBnZW9tX3BvaW50KGRhdGEgPSBwbG90X2dyb3VwLCBhZXMoeSA9IG1lYW4pLCBzaGFwZSA9IDEsIHNpemUgPSAzLCBzdHJva2UgPSAxLjUpICsKICBnZW9tX2xpbmVyYW5nZShkYXRhID0gcGxvdF9ncm91cCwgYWVzKHkgPSBOVUxMLCB5bWluID0gY2lfbG93ZXIsIHltYXggPSBjaV91cHBlciksIGx3ZCA9IDEuMikgKwogICMgZ2VvbV90ZXh0KGRhdGEgPSBucywgYWVzKGxhYmVsID0gbiksIHkgPSAtLjA1LCB4ID0gMiwgY29sID0gImJsYWNrIiwgc2l6ZSA9IDMpICsKICBmYWNldF9ncmlkKHNwZWNpZXMgfiAuLCBzd2l0Y2ggPSAieSIpICsKICB0aGVtZShzdHJpcC50ZXh0LnkgPSBlbGVtZW50X3RleHQoYW5nbGUgPSAxODAsIGhqdXN0ID0gMCkpICsKICBzY2FsZV9zaXplX2FyZWEoIk51bWJlciBvZiBUcmlhbHMiLCBtYXhfc2l6ZSA9IDMsIGJyZWFrcyA9IGMoMTIsIDI0LCAzNikpICsKICBzY2FsZV9jb2xvdXJfc29sYXJpemVkKCJEZWxheSIsIGJyZWFrcyA9IGMoInNob3J0IiwgIm1lZGl1bSIsICJsb25nIikpICsKICBjb29yZF9mbGlwKHlsaW0gPSAwOjEpCmBgYAoKYGBge3J9Cmdnc2F2ZSgiLi4vZ3JhcGhzLzAyXzAxX292ZXJ2aWV3LnBuZyIsIHdpZHRoID0gNCwgaGVpZ2h0ID0gNC41LCBzY2FsZSA9IDIpCmdnc2F2ZSgiLi4vZ3JhcGhzL0ZpZzIudGlmZiIsIHdpZHRoID0gNCwgaGVpZ2h0ID0gNC41LCBzY2FsZSA9IDIsIHR5cGUgPSAiY2Fpcm8iLCBjb21wcmVzc2lvbiA9ICJsenciKQpgYGAKCiMgT3ZlcnZpZXcgYnkgc3BlY2llcyBhY3Jvc3MgZGVsYXlzCgpgYGB7ciBvdmVydmlldyBieSBzcGVjaWVzIGFjcm9zcyBkZWxheXMsIHdhcm5pbmc9RkFMU0V9CnBsb3RfaW5kaXZpZHVhbDIgPC0gcGxvdF9pbmRpdmlkdWFsICU+JQogIGdyb3VwX2J5KGNsYWRlLCBzcGVjaWVzLCBzdWJqZWN0X3NpdGUpICU+JQogIHN1bW1hcmlzZShjb3JyZWN0ID0gbWVhbihjb3JyZWN0KSkKCnBsb3RfZ3JvdXAyIDwtIHBsb3RfaW5kaXZpZHVhbDIgJT4lCiAgIG11bHRpX2Jvb3Rfc3RhbmRhcmQoY29sID0gImNvcnJlY3QiKQpgYGAKCmBgYHtyLCBmaWcud2lkdGg9NCwgZmlnLmhlaWdodD0xLjh9CnAxICsgYWVzKHggPSBmY3RfcmV2KHNwZWNpZXMpLCB5ID0gY29ycmVjdCwgY29sID0gY2xhZGUpICsKICBnZW9tX2ppdHRlcihkYXRhID0gcGxvdF9pbmRpdmlkdWFsMiwgd2lkdGggPSAuMSwgaGVpZ2h0ID0gLjAxNSwgYWxwaGEgPSAuMTUsIHNpemUgPSAzKSArCiAgZ2VvbV9wb2ludChkYXRhID0gcGxvdF9ncm91cDIsIGFlcyh5ID0gbWVhbiksIHNoYXBlID0gMSwgc2l6ZSA9IDMsIHN0cm9rZSA9IDEuNSkgKwogIGdlb21fbGluZXJhbmdlKGRhdGEgPSBwbG90X2dyb3VwMiwgYWVzKHkgPSBOVUxMLCB5bWluID0gY2lfbG93ZXIsIHltYXggPSBjaV91cHBlciksIGx3ZCA9IDEuMikgKwogIGdlb21fdGV4dChkYXRhID0gbnMsIGFlcyhsYWJlbCA9IG4pLCB5ID0gLS4wNSwgY29sID0gImJsYWNrIiwgc2l6ZSA9IDMpICsKICBmYWNldF9ncmlkKGNsYWRlIH4gLiwgc2NhbGVzID0gImZyZWVfeSIsIHNwYWNlID0gImZyZWVfeSIpICsKICB0aGVtZShzdHJpcC50ZXh0LnkgPSBlbGVtZW50X2JsYW5rKCkpICsKICBzY2FsZV9jb2xvdXJfc29sYXJpemVkKCkgKwogIGNvb3JkX2ZsaXAoKQpgYGAKCmBgYHtyfQpnZ3NhdmUoIi4uL2dyYXBocy8wMl8wMl9vdmVydmlld19hY3Jvc3NfZGVsYXlzLnBuZyIsIHdpZHRoID0gNCwgaGVpZ2h0ID0gMS44LCBzY2FsZSA9IDIpCmBgYAoKIyBQbG90cyBieSBzaXRlCgpIZXJlIHdlIHNlbGVjdCB0aGUgc3BlY2llcyBmb3Igd2hpY2ggd2UgaGF2ZSBkYXRhIGZyb20gbXVsdGlwbGUgc2l0ZXMuIFRoaXMgaXMgYSB2ZXJ5IHByZWxpbWluYXJ5IHdheSBvZiBjaGVja2luZyB3aGV0aGVyIHRoZXJlIGlzIGEgbG90IG9mIHZhcmlhdGlvbiBiZXR3ZWVuIHNpdGVzLiBQbG90dGluZyBjb252ZW50aW9ucyBhcmUgdGhlIHNhbWUgYXMgYWJvdmUuCgpGaXJzdCB3ZSBjaGVjayBmb3Igd2hpY2ggc3BlY2llcyB3ZSBoYXZlIGRhdGEgZnJvbSBtb3JlIHRoYW4gb25lIHNpdGU6CgpgYGB7ciBwbG90cyBieSBzaXRlLCByZXN1bHRzPSJhc2lzIn0KbXBfZGF0YSAlPiUKICBncm91cF9ieShzcGVjaWVzKSAlPiUKICBzdW1tYXJpc2Uoc2l0ZXMgPSBuX2Rpc3RpbmN0KHNpdGUpKSAlPiUKICBhcnJhbmdlKGRlc2Moc2l0ZXMpKSAlPiUKICBrbml0cjo6a2FibGUoKQpgYGAKCiMjIENoaW1wYW56ZWVzCgpgYGB7cn0KY2hpbXBfcGxvdF9pbmRpdmlkdWFsIDwtIG1wX2RhdGEgJT4lCiAgZmlsdGVyKHNwZWNpZXMgPT0gIkNoaW1wYW56ZWUiKSAlPiUKICBncm91cF9ieShzaXRlLCBkZWxheSwgc3ViamVjdF9zaXRlKSAlPiUKICBzdW1tYXJpc2UoY29ycmVjdCA9IG1lYW4oY29ycmVjdCkpCgpjaGltcF9wbG90X2dyb3VwIDwtIGNoaW1wX3Bsb3RfaW5kaXZpZHVhbCAlPiUKICAgbXVsdGlfYm9vdF9zdGFuZGFyZChjb2wgPSAiY29ycmVjdCIpCmBgYAoKYGBge3IsIGZpZy53aWR0aD00LCBmaWcuaGVpZ2h0PTIuNX0KY2ggPC0gcDIgKyBhZXMoeCA9IGRlbGF5LCB5ID0gY29ycmVjdCwgY29sID0gZGVsYXkpICsKICBnZW9tX2ppdHRlcihkYXRhID0gY2hpbXBfcGxvdF9pbmRpdmlkdWFsLCB3aWR0aCA9IC4xLCBoZWlnaHQgPSAuMDE1LCBhbHBoYSA9IC4zLCBzaXplID0gMykgKwogIGdlb21fcG9pbnRyYW5nZShkYXRhID0gY2hpbXBfcGxvdF9ncm91cCwgYWVzKHkgPSBtZWFuLCB5bWluID0gY2lfbG93ZXIsIHltYXggPSBjaV91cHBlciksIHNpemUgPSAuOCwgc2hhcGUgPSAxLCBzdHJva2UgPSAxLjUpICsKICBnZW9tX3RleHQoZGF0YSA9IGZpbHRlcihucywgc3BlY2llcyA9PSAiQ2hpbXBhbnplZSIpLCBhZXMobGFiZWwgPSBuX2J5X3NpdGUpLCB5ID0gLS4wNSwgeCA9IDIsIGNvbCA9ICJibGFjayIsIHNpemUgPSAzKSArCiAgZmFjZXRfZ3JpZChzaXRlIH4gLiwgc3dpdGNoID0gInkiKSArCiAgdGhlbWUoc3RyaXAudGV4dC55ID0gZWxlbWVudF90ZXh0KGFuZ2xlID0gMTgwKSkgKwogIHNjYWxlX2NvbG91cl9zb2xhcml6ZWQoYnJlYWtzID0gYygic2hvcnQiLCAibWVkaXVtIiwgImxvbmciKSkgKwogIGdndGl0bGUoIkNoaW1wYW56ZWVzIikgKwogIGNvb3JkX2ZsaXAoKQoKY2gKYGBgCgojIyBSaW5nLXRhaWxlZCBsZW11cnMKCmBgYHtyfQpydGxlbXVyX3Bsb3RfaW5kaXZpZHVhbCA8LSBtcF9kYXRhICU+JQogIGZpbHRlcihzcGVjaWVzID09ICJSaW5nLXRhaWxlZCBsZW11ciIpICU+JQogIGdyb3VwX2J5KHNpdGUsIGRlbGF5LCBzdWJqZWN0X3NpdGUpICU+JQogIHN1bW1hcmlzZShjb3JyZWN0ID0gbWVhbihjb3JyZWN0KSkKCnJ0bGVtdXJfcGxvdF9ncm91cCA8LSBydGxlbXVyX3Bsb3RfaW5kaXZpZHVhbCAlPiUKICAgbXVsdGlfYm9vdF9zdGFuZGFyZChjb2wgPSAiY29ycmVjdCIpCmBgYAoKYGBge3IsIGZpZy53aWR0aD00LCBmaWcuaGVpZ2h0PTEuMn0KcDIgKyBhZXMoeCA9IGRlbGF5LCB5ID0gY29ycmVjdCwgY29sID0gZGVsYXkpICsKICBnZW9tX2ppdHRlcihkYXRhID0gcnRsZW11cl9wbG90X2luZGl2aWR1YWwsIHdpZHRoID0gLjEsIGhlaWdodCA9IC4wMTUsIGFscGhhID0gLjMsIHNpemUgPSAzKSArCiAgZ2VvbV9wb2ludHJhbmdlKGRhdGEgPSBydGxlbXVyX3Bsb3RfZ3JvdXAsIGFlcyh5ID0gbWVhbiwgeW1pbiA9IGNpX2xvd2VyLCB5bWF4ID0gY2lfdXBwZXIpLCBzaXplID0gLjgsIHNoYXBlID0gMSwgc3Ryb2tlID0gMS41KSArCiAgZ2VvbV90ZXh0KGRhdGEgPSBmaWx0ZXIobnMsIHNwZWNpZXMgPT0gIlJpbmctdGFpbGVkIGxlbXVyIiksIGFlcyhsYWJlbCA9IG5fYnlfc2l0ZSksIHkgPSAtLjA1LCB4ID0gMiwgY29sID0gImJsYWNrIiwgc2l6ZSA9IDMpICsKICBmYWNldF9ncmlkKHNpdGUgfiAuLCBzd2l0Y2ggPSAieSIpICsKICB0aGVtZShzdHJpcC50ZXh0LnkgPSBlbGVtZW50X3RleHQoYW5nbGUgPSAxODApKSArCiAgc2NhbGVfY29sb3VyX3NvbGFyaXplZChicmVha3MgPSBjKCJzaG9ydCIsICJtZWRpdW0iLCAibG9uZyIpKSArCiAgZ2d0aXRsZSgiUmluZy10YWlsZWQgbGVtdXJzIikgKwogIGNvb3JkX2ZsaXAoKQpgYGAKCmBgYHtyfQpnZ3NhdmUoIi4uL2dyYXBocy8wMl8wNF9ydGxlbXVyX2J5X3NpdGUucG5nIiwgd2lkdGggPSA0LCBoZWlnaHQgPSAxLjIsIHNjYWxlID0gMikKYGBgCgoKIyMgQnJvd24gQ2FwdWNoaW5zCgpgYGB7cn0KY2FwX3Bsb3RfaW5kaXZpZHVhbCA8LSBtcF9kYXRhICU+JQogIGZpbHRlcihzcGVjaWVzID09ICJCcm93biBjYXB1Y2hpbiBtb25rZXkiKSAlPiUKICBncm91cF9ieShzaXRlLCBkZWxheSwgc3ViamVjdF9zaXRlKSAlPiUKICBzdW1tYXJpc2UoY29ycmVjdCA9IG1lYW4oY29ycmVjdCkpCgpjYXBfcGxvdF9ncm91cCA8LSBjYXBfcGxvdF9pbmRpdmlkdWFsICU+JQogICBtdWx0aV9ib290X3N0YW5kYXJkKGNvbCA9ICJjb3JyZWN0IikKYGBgCgpgYGB7ciwgZmlnLndpZHRoPTQsIGZpZy5oZWlnaHQ9MS4yfQpjYXAgPC0gcDIgKyBhZXMoeCA9IGRlbGF5LCB5ID0gY29ycmVjdCwgY29sID0gZGVsYXkpICsKICBnZW9tX2ppdHRlcihkYXRhID0gY2FwX3Bsb3RfaW5kaXZpZHVhbCwgd2lkdGggPSAuMSwgaGVpZ2h0ID0gLjAxNSwgYWxwaGEgPSAuMywgc2l6ZSA9IDMpICsKICBnZW9tX3BvaW50cmFuZ2UoZGF0YSA9IGNhcF9wbG90X2dyb3VwLCBhZXMoeSA9IG1lYW4sIHltaW4gPSBjaV9sb3dlciwgeW1heCA9IGNpX3VwcGVyKSwgc2l6ZSA9IC44LCBzaGFwZSA9IDEsIHN0cm9rZSA9IDEuNSkgKwogIGdlb21fdGV4dChkYXRhID0gZmlsdGVyKG5zLCBzcGVjaWVzID09ICJCcm93biBjYXB1Y2hpbiBtb25rZXkiKSwgYWVzKGxhYmVsID0gbl9ieV9zaXRlKSwgeSA9IC0uMDUsIHggPSAyLCBjb2wgPSAiYmxhY2siLCBzaXplID0gMykgKwogIGZhY2V0X2dyaWQoc2l0ZSB+IC4sIHN3aXRjaCA9ICJ5IikgKwogIHRoZW1lKHN0cmlwLnRleHQueSA9IGVsZW1lbnRfdGV4dChhbmdsZSA9IDE4MCkpICsKICBzY2FsZV9jb2xvdXJfc29sYXJpemVkKGJyZWFrcyA9IGMoInNob3J0IiwgIm1lZGl1bSIsICJsb25nIikpICsKICBnZ3RpdGxlKCJDYXB1Y2hpbiBNb25rZXlzIikgKwogIGNvb3JkX2ZsaXAoKQpgYGAKCmBgYHtyfQpsYXkgPC0gbWF0cml4KGMoMSwgMSwgMikpCmBgYAoKYGBge3IsIGZpZy53aWR0aD00LCBmaWcuaGVpZ2h0PTR9CmdyaWQuYXJyYW5nZShjaCwgY2FwLCBsYXlvdXRfbWF0cml4ID0gbGF5KQpgYGAKCmBgYHtyfQpnZ3NhdmUoIi4uL2dyYXBocy8wMl8wNV9jaGltcF9jYXB1Y2hpbl9ieV9zaXRlLnBuZyIsIGFycmFuZ2VHcm9iKGNoLCBjYXAsIGxheW91dF9tYXRyaXggPSBsYXkpLCB3aWR0aCA9IDQsIGhlaWdodCA9IDQsIHNjYWxlID0gMikKZ2dzYXZlKCIuLi9ncmFwaHMvRmlnNS50aWZmIiwgYXJyYW5nZUdyb2IoY2gsIGNhcCwgbGF5b3V0X21hdHJpeCA9IGxheSksIHdpZHRoID0gNCwgaGVpZ2h0ID0gNCwgc2NhbGUgPSAyLCB0eXBlID0gImNhaXJvIiwgY29tcHJlc3Npb24gPSAibHp3IikKYGBgCgojIyBCb25vYm9zCgpgYGB7cn0KYm9uX3Bsb3RfaW5kaXZpZHVhbCA8LSBtcF9kYXRhICU+JQogIGZpbHRlcihzcGVjaWVzID09ICJCb25vYm8iKSAlPiUKICBncm91cF9ieShzaXRlLCBkZWxheSwgc3ViamVjdF9zaXRlKSAlPiUKICBzdW1tYXJpc2UoY29ycmVjdCA9IG1lYW4oY29ycmVjdCkpCgpib25fcGxvdF9ncm91cCA8LSBib25fcGxvdF9pbmRpdmlkdWFsICU+JQogICBtdWx0aV9ib290X3N0YW5kYXJkKGNvbCA9ICJjb3JyZWN0IikKYGBgCgpgYGB7ciwgZmlnLndpZHRoPTQsIGZpZy5oZWlnaHQ9MS4yfQpwMiArIGFlcyh4ID0gZGVsYXksIHkgPSBjb3JyZWN0LCBjb2wgPSBkZWxheSkgKwogIGdlb21faml0dGVyKGRhdGEgPSBib25fcGxvdF9pbmRpdmlkdWFsLCB3aWR0aCA9IC4xLCBoZWlnaHQgPSAuMDE1LCBhbHBoYSA9IC4zLCBzaXplID0gMykgKwogIGdlb21fcG9pbnRyYW5nZShkYXRhID0gYm9uX3Bsb3RfZ3JvdXAsIGFlcyh5ID0gbWVhbiwgeW1pbiA9IGNpX2xvd2VyLCB5bWF4ID0gY2lfdXBwZXIpLCBzaXplID0gLjgsIHNoYXBlID0gMSwgc3Ryb2tlID0gMS41KSArCiAgZ2VvbV90ZXh0KGRhdGEgPSBmaWx0ZXIobnMsIHNwZWNpZXMgPT0gIkJvbm9ibyIpLCBhZXMobGFiZWwgPSBuX2J5X3NpdGUpLCB5ID0gLS4wNSwgeCA9IDIsIGNvbCA9ICJibGFjayIsIHNpemUgPSAzKSArCiAgZmFjZXRfZ3JpZChzaXRlIH4gLiwgc3dpdGNoID0gInkiKSArCiAgdGhlbWUoc3RyaXAudGV4dC55ID0gZWxlbWVudF90ZXh0KGFuZ2xlID0gMTgwKSkgKwogIHNjYWxlX2NvbG91cl9zb2xhcml6ZWQoYnJlYWtzID0gYygic2hvcnQiLCAibWVkaXVtIiwgImxvbmciKSkgKwogIGdndGl0bGUoIkJvbm9ib3MiKSArCiAgY29vcmRfZmxpcCgpCmBgYAoKYGBge3J9Cmdnc2F2ZSgiLi4vZ3JhcGhzLzAyXzA2X2Jvbm9ib19ieV9zaXRlLnBuZyIsIHdpZHRoID0gNCwgaGVpZ2h0ID0gMS4yLCBzY2FsZSA9IDIpCmBgYAoKIyMgR29yaWxsYQoKYGBge3J9Cmdvcl9wbG90X2luZGl2aWR1YWwgPC0gbXBfZGF0YSAlPiUKICBmaWx0ZXIoc3BlY2llcyA9PSAiR29yaWxsYSIpICU+JQogIGdyb3VwX2J5KHNpdGUsIGRlbGF5LCBzdWJqZWN0X3NpdGUpICU+JQogIHN1bW1hcmlzZShjb3JyZWN0ID0gbWVhbihjb3JyZWN0KSkKCmdvcl9wbG90X2dyb3VwIDwtIGdvcl9wbG90X2luZGl2aWR1YWwgJT4lCiAgIG11bHRpX2Jvb3Rfc3RhbmRhcmQoY29sID0gImNvcnJlY3QiKQpgYGAKCmBgYHtyLCBmaWcud2lkdGg9NCwgZmlnLmhlaWdodD0xLjJ9CnAyICsgYWVzKHggPSBkZWxheSwgeSA9IGNvcnJlY3QsIGNvbCA9IGRlbGF5KSArCiAgZ2VvbV9qaXR0ZXIoZGF0YSA9IGdvcl9wbG90X2luZGl2aWR1YWwsIHdpZHRoID0gLjEsIGhlaWdodCA9IC4wMTUsIGFscGhhID0gLjMsIHNpemUgPSAzKSArCiAgZ2VvbV9wb2ludHJhbmdlKGRhdGEgPSBnb3JfcGxvdF9ncm91cCwgYWVzKHkgPSBtZWFuLCB5bWluID0gY2lfbG93ZXIsIHltYXggPSBjaV91cHBlciksIHNpemUgPSAuOCwgc2hhcGUgPSAxLCBzdHJva2UgPSAxLjUpICsKICBnZW9tX3RleHQoZGF0YSA9IGZpbHRlcihucywgc3BlY2llcyA9PSAiR29yaWxsYSIpLCBhZXMobGFiZWwgPSBuX2J5X3NpdGUpLCB5ID0gLS4wNSwgeCA9IDIsIGNvbCA9ICJibGFjayIsIHNpemUgPSAzKSArCiAgZmFjZXRfZ3JpZChzaXRlIH4gLiwgc3dpdGNoID0gInkiKSArCiAgdGhlbWUoc3RyaXAudGV4dC55ID0gZWxlbWVudF90ZXh0KGFuZ2xlID0gMTgwKSkgKwogIHNjYWxlX2NvbG91cl9zb2xhcml6ZWQoYnJlYWtzID0gYygic2hvcnQiLCAibWVkaXVtIiwgImxvbmciKSkgKwogIGdndGl0bGUoIkdvcmlsbGFzIikgKwogIGNvb3JkX2ZsaXAoKQpgYGAKCmBgYHtyfQpnZ3NhdmUoIi4uL2dyYXBocy8wMl8wN19nb3JpbGxhX2J5X3NpdGUucG5nIiwgd2lkdGggPSA0LCBoZWlnaHQgPSAxLjIsIHNjYWxlID0gMikKYGBgCgojIFRhc2stZXhwZXJpZW5jZQoKSGVyZSB3ZSBzcGxpdCBlYWNoIHNwZWNpZXMgYnkgdGFzayBleHBlcmllbmNlLiBDaGVjayBpZiB3ZSBoYXZlIHNwZWNpZXMgd2l0aCBzdWZmaWNpZW50IG1lbWViZXJzIGhhdmluZyBkaWZmZXJlbnQgbGV2ZWxzIG9mIHRhc2sgZXhwZXJpZW5jZS4KCmBgYHtyIHBsb3RzIGJ5IHRhc2sgZXhwZXJpZW5jZSwgcmVzdWx0cz0iYXNpcyJ9Cm1wX2RhdGEgJT4lCiAgZ3JvdXBfYnkoc3BlY2llcykgJT4lCiAgbXV0YXRlKGx2bHNfdGFza19leHAgPSBuX2Rpc3RpbmN0KHRhc2tfZXhwZXJpZW5jZSkpICU+JQogIGZpbHRlcihsdmxzX3Rhc2tfZXhwID4gMSkgJT4lCiAgZ3JvdXBfYnkoc3BlY2llcywgdGFza19leHBlcmllbmNlKSAlPiUKICBzdW1tYXJpc2UobiA9IG5fZGlzdGluY3Qoc3ViamVjdF9zaXRlKSkgJT4lCiAga25pdHI6OmthYmxlKCkKYGBgCgpgYGB7cn0KIyBnZXQgc2FtcGxlIHNpemVzIChmb3IgdXNlIGluIHBsb3RzKQpuc190YXNrX2V4cCA8LSBtcF9kYXRhICU+JQogIGdyb3VwX2J5KGNsYWRlLCBzcGVjaWVzKSAlPiUKICBtdXRhdGUobiA9IG5fZGlzdGluY3QodGFza19leHBlcmllbmNlKSkgJT4lCiAgZ3JvdXBfYnkoY2xhZGUsIHNwZWNpZXMsIHRhc2tfZXhwZXJpZW5jZSwgbikgJT4lCiAgc3VtbWFyaXNlKG5fYnlfc2l0ZSA9IG5fZGlzdGluY3Qoc3ViamVjdF9zaXRlKSkKYGBgCgpTbyBmYXIsIHRoaXMgb25seSBhcHBsaWVzIHRvIGNoaW1wcyBhbmQgYm9ub2Jvcy4gSG93ZXZlciwgdGFzayBleHBlcmllbmNlIGNvLXZhcmllcyB3aXRoIHNpdGUuCgo8IS0tICMjIENoaW1wYW56ZWVzIC0tPgoKYGBge3J9CmNoaW1wX3Rhc2tfcGxvdF9pbmRpdmlkdWFsIDwtIG1wX2RhdGEgJT4lCiAgZmlsdGVyKHNwZWNpZXMgPT0gIkNoaW1wYW56ZWUiKSAlPiUKICBncm91cF9ieShzaXRlLCB0YXNrX2V4cGVyaWVuY2UsIGRlbGF5LCBzdWJqZWN0X3NpdGUpICU+JQogIHN1bW1hcmlzZShjb3JyZWN0ID0gbWVhbihjb3JyZWN0KSkKCmNoaW1wX3Rhc2tfcGxvdF9ncm91cCA8LSBtcF9kYXRhICU+JQogIGZpbHRlcihzcGVjaWVzID09ICJDaGltcGFuemVlIikgJT4lCiAgZ3JvdXBfYnkodGFza19leHBlcmllbmNlLCBkZWxheSwgc3ViamVjdF9zaXRlKSAlPiUKICBzdW1tYXJpc2UoY29ycmVjdCA9IG1lYW4oY29ycmVjdCkpICU+JQogIG11bHRpX2Jvb3Rfc3RhbmRhcmQoY29sID0gImNvcnJlY3QiKQpgYGAKCmBgYHtyLCBmaWcud2lkdGg9NCwgZmlnLmhlaWdodD0xLjJ9CnBfdGFza2V4cF9jaGltcCA8LSBwMiArIGFlcyh4ID0gZGVsYXksIHkgPSBjb3JyZWN0KSArCiAgZ2VvbV9qaXR0ZXIoZGF0YSA9IGNoaW1wX3Rhc2tfcGxvdF9pbmRpdmlkdWFsLCBhZXMoY29sID0gc2l0ZSksIHdpZHRoID0gLjMsIGhlaWdodCA9IC4wMTUsIGFscGhhID0gLjUsIHNpemUgPSAzKSArCiAgZ2VvbV9wb2ludHJhbmdlKGRhdGEgPSBjaGltcF90YXNrX3Bsb3RfZ3JvdXAsIGFlcyh5ID0gbWVhbiwgeW1pbiA9IGNpX2xvd2VyLCB5bWF4ID0gY2lfdXBwZXIsIHNoYXBlID0gZGVsYXkpLCBzaXplID0gLjgsIHN0cm9rZSA9IDEuNSkgKwogIGdlb21fdGV4dChkYXRhID0gZmlsdGVyKG5zX3Rhc2tfZXhwLCBzcGVjaWVzID09ICJDaGltcGFuemVlIiksIGFlcyhsYWJlbCA9IG5fYnlfc2l0ZSksIHkgPSAtLjA1LCB4ID0gMiwgY29sID0gImJsYWNrIiwgc2l6ZSA9IDMpICsKICBmYWNldF9ncmlkKHRhc2tfZXhwZXJpZW5jZSB+IC4sIHN3aXRjaCA9ICJ5IikgKwogIHRoZW1lKGxlZ2VuZC5ib3ggPSAiaG9yaXpvbnRhbCIsIHN0cmlwLnRleHQueSA9IGVsZW1lbnRfdGV4dChhbmdsZSA9IDE4MCkpICsKICBzY2FsZV9zaGFwZV9tYW51YWwodmFsdWVzID0gYygxLCAyLCA1KSwgYnJlYWtzID0gYygic2hvcnQiLCAibWVkaXVtIiwgImxvbmciKSkgKwogIHNjYWxlX2NvbG91cl9zb2xhcml6ZWQoKSArCiAgZ2d0aXRsZSgiQ2hpbXBhbnplZXMiKSArCiAgY29vcmRfZmxpcCgpCmBgYAoKPCEtLSAjIyBCb25vYm9zIC0tPgoKYGBge3J9CmJvbm9ib190YXNrX3Bsb3RfaW5kaXZpZHVhbCA8LSBtcF9kYXRhICU+JQogIGZpbHRlcihzcGVjaWVzID09ICJCb25vYm8iKSAlPiUKICBncm91cF9ieShzaXRlLCB0YXNrX2V4cGVyaWVuY2UsIGRlbGF5LCBzdWJqZWN0X3NpdGUpICU+JQogIHN1bW1hcmlzZShjb3JyZWN0ID0gbWVhbihjb3JyZWN0KSkKCmJvbm9ib190YXNrX3Bsb3RfZ3JvdXAgPC0gbXBfZGF0YSAlPiUKICBmaWx0ZXIoc3BlY2llcyA9PSAiQm9ub2JvIikgJT4lCiAgZ3JvdXBfYnkodGFza19leHBlcmllbmNlLCBkZWxheSwgc3ViamVjdF9zaXRlKSAlPiUKICBzdW1tYXJpc2UoY29ycmVjdCA9IG1lYW4oY29ycmVjdCkpICU+JQogIG11bHRpX2Jvb3Rfc3RhbmRhcmQoY29sID0gImNvcnJlY3QiKQpgYGAKCmBgYHtyLCBmaWcud2lkdGg9NCwgZmlnLmhlaWdodD0xLjJ9CnBfdGFza2V4cF9ib24gPC0gcDIgKyBhZXMoeCA9IGRlbGF5LCB5ID0gY29ycmVjdCkgKwogIGdlb21faml0dGVyKGRhdGEgPSBib25vYm9fdGFza19wbG90X2luZGl2aWR1YWwsIGFlcyhjb2wgPSBzaXRlKSwgd2lkdGggPSAuMywgaGVpZ2h0ID0gLjAxNSwgYWxwaGEgPSAuNSwgc2l6ZSA9IDMpICsKICBnZW9tX3BvaW50cmFuZ2UoZGF0YSA9IGJvbm9ib190YXNrX3Bsb3RfZ3JvdXAsIGFlcyh5ID0gbWVhbiwgeW1pbiA9IGNpX2xvd2VyLCB5bWF4ID0gY2lfdXBwZXIsIHNoYXBlID0gZGVsYXkpLCBzaXplID0gLjgsIHN0cm9rZSA9IDEuNSkgKwogIGdlb21fdGV4dChkYXRhID0gZmlsdGVyKG5zX3Rhc2tfZXhwLCBzcGVjaWVzID09ICJCYm9ub2JvIiksIGFlcyhsYWJlbCA9IG5fYnlfc2l0ZSksIHkgPSAtLjA1LCB4ID0gMiwgY29sID0gImJsYWNrIiwgc2l6ZSA9IDMpICsKICBmYWNldF9ncmlkKHRhc2tfZXhwZXJpZW5jZSB+IC4sIHN3aXRjaCA9ICJ5IikgKwogIHRoZW1lKGxlZ2VuZC5ib3ggPSAiaG9yaXpvbnRhbCIsIHN0cmlwLnRleHQueSA9IGVsZW1lbnRfdGV4dChhbmdsZSA9IDE4MCkpICsKICBzY2FsZV9zaGFwZV9tYW51YWwodmFsdWVzID0gYygxLCAyLCA1KSwgYnJlYWtzID0gYygic2hvcnQiLCAibWVkaXVtIiwgImxvbmciKSkgKwogIHNjYWxlX2NvbG91cl9zb2xhcml6ZWQoKSArCiAgZ2d0aXRsZSgiQm9ub2JvcyIpICsKICBjb29yZF9mbGlwKCkKYGBgCgpgYGB7ciwgZmlnLndpZHRoPTQsIGZpZy5oZWlnaHQ9M30KZ3JpZC5hcnJhbmdlKHBfdGFza2V4cF9jaGltcCwgcF90YXNrZXhwX2JvbiwgbmNvbCA9IDEpCmBgYAoKYGBge3J9Cmdyb2IgPC0gYXJyYW5nZUdyb2IocF90YXNrZXhwX2NoaW1wLCBwX3Rhc2tleHBfYm9uLCBuY29sID0gMSkKZ2dzYXZlKCIuLi9ncmFwaHMvMDJfMDhfdGFza19leHBlcmllbmNlLnBuZyIsIGdyb2IsIHdpZHRoID0gNCwgaGVpZ2h0ID0gMywgc2NhbGUgPSAyKQpgYGAKCgojIEFnZQoKSGVyZSB3ZSBwbG90IGFnZSBhZ2FpbnN0IGNvcnJlY3QgY2hvaWNlIHNlcGFyYXRlIGZvciBlYWNoIGRlbGF5IGFuZCBzcGVjaWVzLiBSZWdyZXNzaW9uIGxpbmUgaXMgc21vb3RoZWQgZGVsYXlhbCBtZWFuLgoKYGBge3J9CnBsb3RfYWdlIDwtIG1wX2RhdGEgJT4lCiAgbXV0YXRlKGRlbGF5ID0gZmN0X3JldihkZWxheSkpICU+JQogIGdyb3VwX2J5KHN1YmplY3Rfc2l0ZSwgbm9ybV9hZ2UsIGNsYWRlLCBzcGVjaWVzLCBkZWxheSkgJT4lCiAgc3VtbWFyaXNlKGNvcnJlY3QgPSBtZWFuKGNvcnJlY3QpKQpgYGAKCmBgYHtyLCBmaWcud2lkdGg9NCwgZmlnLmhlaWdodD0xLjN9CmdncGxvdChwbG90X2FnZSwgYWVzKHggPSBub3JtX2FnZSwgeSA9IGNvcnJlY3QpKSArCiAgZ2VvbV9qaXR0ZXIoYWVzKGNvbCA9IGNsYWRlKSwgd2lkdGggPSAuMDUsIGhlaWdodCA9IC4wNSwgYWxwaGEgPSAuNSwgc2l6ZSA9IDIuNSkgKwogIGdlb21fc21vb3RoKG1ldGhvZCA9ICJnbG0iLCBtZXRob2QuYXJncyA9IGxpc3QoZmFtaWx5ID0gImJpbm9taWFsIiksIGNvbCA9ICJibGFjayIpICsKICAjIGdlb21fdmxpbmUoeGludGVyY2VwdCA9IDAsIGx0eSA9IDIpICsKICBnZW9tX2hsaW5lKHlpbnRlcmNlcHQgPSAxLzMsIGx0eSA9IDIpICsKICBmYWNldF9ncmlkKH4gZGVsYXkpICsKICBsYWJzKHggPSAiTm9ybWVkIEFnZSAocmVsYXRpdmUgdG8gc3BlY2llcyBsb25nZXZpdHkpIiwgeSA9ICJQcm9wb3J0aW9uIENvcnJlY3QiKSArCiAgc2NhbGVfY29sb3Jfc29sYXJpemVkKCJDbGFkZSIpICsKICB5bGltKGMoLS4wNSwgMS4wNSkpCmBgYAoKYGBge3J9Cmdnc2F2ZSgiLi4vZ3JhcGhzLzAyXzA5X2FnZV9ieV9kZWxheS5wbmciLCB3aWR0aCA9IDQsIGhlaWdodCA9IDEuMywgc2NhbGUgPSAyKQpnZ3NhdmUoIi4uL2dyYXBocy9GaWc0LnRpZmYiLCB3aWR0aCA9IDQsIGhlaWdodCA9IDEuMywgc2NhbGUgPSAyLCB0eXBlID0gImNhaXJvIiwgY29tcHJlc3Npb24gPSAibHp3IikKYGBgCgpgYGB7ciwgZmlnLndpZHRoPTQsIGZpZy5oZWlnaHQ9My40fQpnZ3Bsb3QocGxvdF9hZ2UsIGFlcyh4ID0gbm9ybV9hZ2UsIHkgPSBjb3JyZWN0KSkgKwogIGdlb21faml0dGVyKGFlcyhmaWxsID0gc3BlY2llcyksIHdpZHRoID0gLjA1LCBoZWlnaHQgPSAuMDUsIGFscGhhID0gLjUsIHNpemUgPSAyLjUsIHNoYXBlID0gMjEsIHN0cm9rZSA9IDApICsKICBnZW9tX3Ntb290aChhZXMoY29sID0gZGVsYXkpLCBtZXRob2QgPSAiZ2xtIiwgbWV0aG9kLmFyZ3MgPSBsaXN0KGZhbWlseSA9ICJiaW5vbWlhbCIpLCBzaG93LmxlZ2VuZCA9IEYpICsKICBnZW9tX3ZsaW5lKHhpbnRlcmNlcHQgPSAwLCBsdHkgPSAyKSArCiAgZ2VvbV9obGluZSh5aW50ZXJjZXB0ID0gMS8zLCBsdHkgPSAyKSArCiAgZmFjZXRfZ3JpZChkZWxheSB+IGNsYWRlLCBzY2FsZXMgPSAiZnJlZV94IikgKwogIGxhYnMoeCA9ICJOb3JtZWQgQWdlIChyZWxhdGl2ZSB0byBzcGVjaWVzIGxvbmdldml0eSkiLCB5ID0gIlByb3BvcnRpb24gQ29ycmVjdCIpICsKICB0aGVtZShsZWdlbmQucG9zaXRpb24gPSAiYm90dG9tIikgKwogIHNjYWxlX2NvbG9yX21hbnVhbCh2YWx1ZXMgPSByZXYoc29sYXJpemVkX3BhbGV0dGUoMykpKSArCiAgeWxpbShjKC0uMDUsIDEuMDUpKQpgYGAKCmBgYHtyfQpnZ3NhdmUoIi4uL2dyYXBocy8wMl8xMF9hZ2VfYnlfZGVsYXlfc3BlY2llcy5wbmciLCB3aWR0aCA9IDQsIGhlaWdodCA9IDMuNCwgc2NhbGUgPSAyKQpgYGAKCgojIEN1cCBkaXN0YW5jZSBhbmQgYm9hcmQgc2l6ZQoKKHNpZy4gZWZmZWN0IGluIG1vZGVsKQoKYGBge3J9CnBsb3RfY3VwIDwtIG1wX2RhdGEgJT4lCiAgIGdyb3VwX2J5KHN1YmplY3Rfc2l0ZSwgY3VwX2Rpc3RhbmNlLCBjbGFkZSwgc3BlY2llcywgZGVsYXkpICU+JQogICBzdW1tYXJpc2UoY29ycmVjdCA9IG1lYW4oY29ycmVjdCkpCmBgYAoKYGBge3IgcGxvdHRpbmcgY3VwIGRpc3RhbmNlfQpjcCA8LSBnZ3Bsb3QocGxvdF9jdXAsIGFlcyh4ID0gY3VwX2Rpc3RhbmNlLCB5ID0gY29ycmVjdCkpICsKICBnZW9tX2ppdHRlcihhZXMoY29sID0gY2xhZGUpLCB3aWR0aCA9IC41LCBoZWlnaHQgPSAuMDUsIGFscGhhID0gLjUsIHNpemUgPSAyKSArCiAgZ2VvbV9zbW9vdGgobWV0aG9kID0gImdsbSIsIG1ldGhvZC5hcmdzID0gbGlzdChmYW1pbHkgPSAiYmlub21pYWwiKSwgY29sID0gImJsYWNrIikgKwogIGxhYnMoeCA9ICJDdXAgRGlzdGFuY2UgaW4gY20iLCB5ID0gIlByb3BvcnRpb24gQ29ycmVjdCIpICsKICBnZW9tX2hsaW5lKHlpbnRlcmNlcHQgPSAxLzMsIGx0eSA9IDIpICsKICBzY2FsZV9jb2xvcl9zb2xhcml6ZWQoIkNsYWRlIikgKwogIHlsaW0oYygtLjA1LCAxLjA1KSkgKwogIGdndGl0bGUoIkEuIEN1cCBEaXN0YW5jZSIpCmBgYAoKYGBge3J9CnBsb3RfYm9hcmQgPC0gbXBfZGF0YSAlPiUKICAgZ3JvdXBfYnkoc3ViamVjdF9zaXRlLCBib2FyZF9zaXplLCBjbGFkZSwgc3BlY2llcywgZGVsYXkpICU+JQogICBzdW1tYXJpc2UoY29ycmVjdCA9IG1lYW4oY29ycmVjdCkpCmBgYAoKYGBge3IgcGxvdHRpbmcgYm9hcmQgc2l6ZX0KYnAgPC0gZ2dwbG90KHBsb3RfYm9hcmQsIGFlcyh4ID0gYm9hcmRfc2l6ZSwgeSA9IGNvcnJlY3QpKSArCiAgZ2VvbV9qaXR0ZXIoYWVzKGNvbCA9IGNsYWRlKSwgd2lkdGggPSAuNSwgaGVpZ2h0ID0gLjA1LCBhbHBoYSA9IC41LCBzaXplID0gMikgKwogIGdlb21fc21vb3RoKG1ldGhvZCA9ICJnbG0iLCBtZXRob2QuYXJncyA9IGxpc3QoZmFtaWx5ID0gImJpbm9taWFsIiksIGNvbCA9ICJibGFjayIpICsKICBsYWJzKHggPSAiQm9hcmQgU2l6ZSBpbiBjbSIsIHkgPSAiUHJvcG9ydGlvbiBDb3JyZWN0IikgKwogIGdlb21faGxpbmUoeWludGVyY2VwdCA9IDEvMywgbHR5ID0gMikgKwogIHNjYWxlX2NvbG9yX3NvbGFyaXplZCgiQ2xhZGUiKSArCiAgeWxpbShjKC0uMDUsIDEuMDUpKSArCiAgZ2d0aXRsZSgiQi4gQm9hcmQgU2l6ZSIpCmBgYAoKYGBge3J9CnBjYiA8LSBnZ2FycmFuZ2UoY3AsIGJwLCBjb21tb24ubGVnZW5kID0gVCwgbGVnZW5kID0gInJpZ2h0IikKYGBgCgpgYGB7ciwgZmlnLndpZHRoPTgsIGZpZy5oZWlnaHQ9M30KcGNiCmBgYAoKYGBge3J9Cmdnc2F2ZSgiLi4vZ3JhcGhzLzAyXzExX2N1cF9kaXN0YW5jZV9ib2FyZF9zaXplLnBuZyIsIHBjYiwgd2lkdGggPSA4LCBoZWlnaHQgPSAzLCBzY2FsZSA9IDEuNSkKYGBgCgo=